package org.chipu.shared.util.file;

import java.io.*;
import java.util.LinkedList;
import java.util.List;

/**
 * The encoding of the text file MUST maps a single byte to a char.
 * @author sabre150
 */
public class BidirectionalReader {
	static class BackwardsFileInputStream extends InputStream {
		private final byte[] buffer = new byte[4096];
		private final RandomAccessFile raf;
		private long currentPositionInFile;
		private int currentPositionInBuffer;

		public BackwardsFileInputStream(File file) throws IOException {
			assert (file != null) && file.exists() && file.isFile() && file.canRead();

			raf = new RandomAccessFile(file, "r");
			currentPositionInFile = raf.length();
			currentPositionInBuffer = 0;
		}

		@Override
		public int read() throws IOException {
			if (currentPositionInFile <= 0)
				return -1;
			if (--currentPositionInBuffer < 0) {
				currentPositionInBuffer = buffer.length;
				long startOfBlock = currentPositionInFile - buffer.length;
				if (startOfBlock < 0) {
					currentPositionInBuffer = buffer.length + (int) startOfBlock;
					startOfBlock = 0;
				}
				raf.seek(startOfBlock);
				raf.readFully(buffer, 0, currentPositionInBuffer);
				return read();
			}
			currentPositionInFile--;
			return buffer[currentPositionInBuffer];
		}

		@Override
		public void close() throws IOException {
			raf.close();
		}
	}

	public static List<String> head(File file, int numberOfLinesToRead) throws IOException {
		return head(file, "ISO-8859-1", numberOfLinesToRead);
	}

	public static List<String> head(File file, String encoding, int numberOfLinesToRead) throws IOException {
		assert (file != null) && file.exists() && file.isFile() && file.canRead();
		assert numberOfLinesToRead > 0;
		assert encoding != null;

		LinkedList<String> lines = new LinkedList<String>();
		BufferedReader reader = null;
		try {
			reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), encoding));
			for(String line = null; (numberOfLinesToRead-- > 0) && (line = reader.readLine()) != null;)
				lines.addLast(line);
		} finally {
			if (reader != null)
				reader.close();
		}
		return lines;
	}

	public static List<String> tail(File file, int numberOfLinesToRead) throws IOException {
		return tail(file, "ISO-8859-1", numberOfLinesToRead);
	}

	public static List<String> tail(File file, String encoding, int numberOfLinesToRead) throws IOException {
		assert file != null && file.exists() && file.isFile() && file.canRead() : "File could not be tailed: " + file
			+ (file == null? "": " exist?" + file.exists());
		assert numberOfLinesToRead > 0;
		assert (encoding != null) && encoding.matches("(?i)(iso-8859|ascii|us-ascii).*");

		LinkedList<String> lines = new LinkedList<String>();
		BufferedReader reader = null;
		try {
			reader = new BufferedReader(new InputStreamReader(new BackwardsFileInputStream(file), encoding));
			for(String line = null; (numberOfLinesToRead-- > 0) && (line = reader.readLine()) != null;) {
				// Reverse the order of the characters in the string
				char[] chars = line.toCharArray();
				for(int j = 0, k = chars.length - 1; j < k; j++, k--) {
					char temp = chars[j];
					chars[j] = chars[k];
					chars[k] = temp;
				}
				lines.addFirst(new String(chars));
			}
		} finally {
			if (reader != null)
				reader.close();
		}
		return lines;
	}
}